#pragma once
#include "../Ode.h"

namespace Skill
{
	namespace Ode
	{
		ref class DVector4;
		public ref class DMatrix4 : DIUnmanagedPointer
		{
			INTERNAL_CLASS_DECLARATION(DMatrix4,dReal);
		public:
			UNMANAGEDPOINTER_DECLARATION();

			DMatrix4()
			{
				_SetPointer(new dMatrix4(),true);
			}

			property dReal default[int]
			{
				dReal get(int index);
				void set(int index,dReal value);
			}

			property dReal default[int,int]
			{
				dReal get(int i,int j);
				void set(int i,int j,dReal value);
			}


			VALUE_PROP_DECLARATION_GETSET(dReal,M11);
			VALUE_PROP_DECLARATION_GETSET(dReal,M21);
			VALUE_PROP_DECLARATION_GETSET(dReal,M31);
			VALUE_PROP_DECLARATION_GETSET(dReal,M41);

			VALUE_PROP_DECLARATION_GETSET(dReal,M12);
			VALUE_PROP_DECLARATION_GETSET(dReal,M22);
			VALUE_PROP_DECLARATION_GETSET(dReal,M32);
			VALUE_PROP_DECLARATION_GETSET(dReal,M42);

			VALUE_PROP_DECLARATION_GETSET(dReal,M13);						
			VALUE_PROP_DECLARATION_GETSET(dReal,M23);						
			VALUE_PROP_DECLARATION_GETSET(dReal,M33);						
			VALUE_PROP_DECLARATION_GETSET(dReal,M43);

			VALUE_PROP_DECLARATION_GETSET(dReal,M14);						
			VALUE_PROP_DECLARATION_GETSET(dReal,M24);						
			VALUE_PROP_DECLARATION_GETSET(dReal,M34);						
			VALUE_PROP_DECLARATION_GETSET(dReal,M44);

			///<summary> set a matrix to all zeros </summary>
			void SetZero();

			///<summary> set a matrix to specific value </summary>
			void SetValue(dReal value);

			///<summary> 
			/// do an in-place cholesky decomposition on the lower triangle of the n*n
			/// symmetric matrix A (which is stored by rows). the resulting lower triangle
			/// will be such that L*L'=A. return 1 on success and 0 on failure (on failure
			/// the matrix is not positive definite).			
			///</summary> 
			bool FactorCholesky();

			///<summary> 
			/// solve for x: L*L'*x = b, and put the result back into x.
			/// L is size n*n, b is size n*1. only the lower triangle of L is considered. 
			///</summary> 
			static void SolveCholesky(DMatrix4^ L , DVector4^ %b);

			///<summary> 
			/// compute the inverse of the n*n positive definite matrix A and put it in
			/// Ainv. this is not especially fast. this returns 1 on success (A was
			/// positive definite) or 0 on failure (not PD).
			///</summary> 
			static bool InvertPDMatrix(DMatrix4^ A, DMatrix4^ %Ainv);

			///<summary> 
			/// check whether an n*n matrix A is positive definite, return 1/0 (yes/no).
			/// positive definite means that x'*A*x > 0 for any x. this performs a
			/// cholesky decomposition of A. if the decomposition fails then the matrix
			/// is not positive definite. A is stored by rows. A is not altered.
			///</summary> 
			property bool IsPositiveDefinite
			{
				bool get();
			}

			///<summary> 
			/// factorize a matrix A into L*D*L', where L is lower triangular with ones on
			/// the diagonal, and D is diagonal.
			/// A is an n*n matrix stored by rows, with a leading dimension of n rounded
			/// up to 4. L is written into the strict lower triangle of A (the ones are not
			/// written) and the reciprocal of the diagonal elements of D are written into
			/// d.
			///</summary> 
			static void FactorLDLT(DMatrix4^ A, DMatrix4^ %d,int nskip);

			///<summary> 
			/// solve L*x=b, where L is n*n lower triangular with ones on the diagonal,
			/// and x,b are n*1. b is overwritten with x.
			/// the leading dimension of L is `nskip'.
			///</summary> 
			void SolveL1(DMatrix4^ L, DVector4^ %b,int nskip);

			///<summary> 
			/// solve L'*x=b, where L is n*n lower triangular with ones on the diagonal,
			/// and x,b are n*1. b is overwritten with x.
			/// the leading dimension of L is `nskip'. 
			///</summary> 
			void SolveL1T(DMatrix4^ L, DVector4^ %b,int nskip);



			
			// given `L', a n*n lower triangular matrix with ones on the diagonal,
			// and `d', a n*1 vector of the reciprocal diagonal elements of an n*n matrix
			// D, solve L*D*L'*x=b where x,b are n*1. x overwrites b.
			// the leading dimension of L is `nskip'.			
			//ODE_API void dSolveLDLT (const dReal *L, const dReal *d, dReal *b, int n, int nskip);

			
			// given an L*D*L' factorization of an n*n matrix A, return the updated
			// factorization L2*D2*L2' of A plus the following "top left" matrix:
			//
			//    [ b a' ]     <-- b is a[0]
			//    [ a 0  ]     <-- a is a[1..n-1]
			//
			//   - L has size n*n, its leading dimension is nskip. L is lower triangular
			//     with ones on the diagonal. only the lower triangle of L is referenced.
			//   - d has size n. d contains the reciprocal diagonal elements of D.
			//   - a has size n.
			// the result is written into L, except that the left column of L and d[0]
			// are not actually modified. see ldltaddTL.m for further comments. 			
			//ODE_API void dLDLTAddTL (dReal *L, dReal *d, const dReal *a, int n, int nskip);

			
			// given an L*D*L' factorization of a permuted matrix A, produce a new
			// factorization for row and column `r' removed.
			//   - A has size n1*n1, its leading dimension in nskip. A is symmetric and
			//     positive definite. only the lower triangle of A is referenced.
			//     A itself may actually be an array of row pointers.
			//   - L has size n2*n2, its leading dimension in nskip. L is lower triangular
			//     with ones on the diagonal. only the lower triangle of L is referenced.
			//   - d has size n2. d contains the reciprocal diagonal elements of D.
			//   - p is a permutation vector. it contains n2 indexes into A. each index
			//     must be in the range 0..n1-1.
			//   - r is the row/column of L to remove.
			// the new L will be written within the old L, i.e. will have the same leading
			// dimension. the last row and column of L, and the last element of d, are
			// undefined on exit.			
			// a fast O(n^2) algorithm is used. see ldltremove.m for further comments.			
			//ODE_API void dLDLTRemove (dReal **A, const int *p, dReal *L, dReal *d,
			//	int n1, int n2, int r, int nskip);

			
			// given an n*n matrix A (with leading dimension nskip), remove the r'th row
			// and column by moving elements. the new matrix will have the same leading
			// dimension. the last row and column of A are untouched on exit.			
			//ODE_API void dRemoveRowCol (dReal *A, int n, int nskip, int r);

			virtual String^ ToString() override
			{
				return String::Format("{{{{M11:{0} M12:{1} M13:{2} M14:{3}}} {{M21:{4} M22:{5} M23:{6} M24:{7}}} {{M31:{8} M32:{9} M33:{10} M34:{11}}} {{M41:{12} M42:{13} M43:{14} M44:{15}}}}}",M11,M12,M13,M21,M22,M23,M31,M32,M33,M41,M42,M43);
			}
			
		};
	}
}